#include <QtGui>
#include <QtCore>

#include "touchconv.h"
#include "batchlist.h"
#include "atime.h"

TouchConv::TouchConv(QWidget *parent): QMainWindow(parent),
		ffmpeg("ffmpeg.exe", this),
		total(0)
{
	QGridLayout *l = new QGridLayout();
	
	// batch list
	batchList = new BatchList();
	
	removeItemButton = new QPushButton(tr("remove"));
	clearButton = new QPushButton(tr("clear"));
	upButton = new QPushButton(tr("up"));
	downButton = new QPushButton(tr("down"));
	
	autoRemoveFinishedItem = new QCheckBox(tr("remove finished items"), this);
	
	connect(clearButton, SIGNAL(clicked()), batchList, SLOT(clear()));
	connect(removeItemButton, SIGNAL(clicked()), batchList, SLOT(removeCurrentItem()));
	connect(upButton, SIGNAL(clicked()), batchList, SLOT(upItem()));
	connect(downButton, SIGNAL(clicked()), batchList, SLOT(downItem()));
	
	l->addWidget(batchList, 0, 0, 2, 3);
	l->addWidget(autoRemoveFinishedItem, 2, 0);
	l->addWidget(clearButton, 2, 1);
	l->addWidget(removeItemButton, 2, 2);
	l->addWidget(upButton, 0, 3);
	l->addWidget(downButton, 1, 3);
	
	// output setting
	outputSuffix = new QComboBox();
	outputSuffix->setEditable(true);
	
	outputDirectory = new QLineEdit();
	changeOutputDirectoryButton = new QPushButton(tr("change"));
	
	connect(changeOutputDirectoryButton, SIGNAL(clicked()), this, SLOT(changeOutputDirectory()));
	
	l->addWidget(outputSuffix, 4, 0);
	l->addWidget(outputDirectory, 4, 1, 1, 2);
	l->addWidget(changeOutputDirectoryButton, 4, 3);
	
	// convert profile
	profile = new QComboBox();
	profile->setEditable(false);
	
	connect(profile, SIGNAL(currentIndexChanged(int)), this, SLOT(setSuffixForSelectedProfile(int)));
	
	loadSettingProfiles();
	
	l->addWidget(profile, 3, 0, 1, 3);
	
	// progress
	l->addWidget(new QLabel(tr("current")), 7, 0);
	
	progress = new QLabel(tr("converting: 0/0"));
	totalProgressBar = new QProgressBar();

	currentFile = new QLabel(tr("-"));
	currentProgressBar = new QProgressBar();
	currentProgressBar->setRange(0, 100);
	
	// time for calculate estimated convert time
	timeElapsed = new QLabel(tr("elapsed: 00:00:00"));
	timeRemain = new QLabel(tr("remain: 00:00:00"));
	estimatedTotalTime = new QLabel(tr("total: 00:00:00"));
	
	l->addWidget(progress, 6, 0);
	l->addWidget(totalProgressBar, 6, 1, 1, 2);
	
	l->addWidget(currentProgressBar, 7, 1, 1, 2);
	l->addWidget(currentFile, 8, 1, 1, 2);
	l->addWidget(timeElapsed, 9, 1, 1, 2);
	l->addWidget(timeRemain, 10, 1, 1, 2);
	l->addWidget(estimatedTotalTime, 11, 1, 1, 2);
	
	// operations
	convertButton = new QPushButton(tr("convert"));
	abortButton = new QPushButton(tr("abort"));
	
	abortButton->setEnabled(false);
	
	connect(convertButton, SIGNAL(clicked()), this, SLOT(startConvert()));
	connect(abortButton, SIGNAL(clicked()), this, SLOT(abort()));
	
	l->addWidget(convertButton, 10, 3);
	l->addWidget(abortButton, 11, 3);
	
	// set central widget
	QWidget *c = new QWidget();
	c->setLayout(l);
	setCentralWidget(c);
	
	// FFmpeg
	connect(&ffmpeg, SIGNAL(progressChanged(int)), this, SLOT(updateProgress(int)));
	connect(&ffmpeg, SIGNAL(finished(int, QProcess::ExitStatus)), this, SLOT(processFinished(int, QProcess::ExitStatus)));
	
	// status bar
	setupStatusBar();
	
	// ready
	status->setText(tr("ready"));
}

void TouchConv::closeEvent(QCloseEvent *e)
{
	if (ffmpeg.isRunning())
		ffmpeg.abort();
	
	e->accept();
}

void TouchConv::setupStatusBar()
{
	totalElapsedTime = new QLabel(tr("elapsed: 00:00:00:"));
	status = new QLabel(tr("status"));
	
	statusBar()->addWidget(totalElapsedTime);
	statusBar()->addWidget(status);
}

void TouchConv::processFinished(int rc, QProcess::ExitStatus exitStatus)
{
	// QMessageBox::information(this, "debug", QString("return: %1\nexit status: %2").arg(rc).arg(exitStatus));
	if (exitStatus == QProcess::CrashExit)
		return;
	
	// update total progress
	totalProgressBar->setValue(total - queue.size());
	
	status->setText(tr("finished"));
	currentProgressBar->setValue(currentProgressBar->maximum());
	
	// remove finished item from list
	if (autoRemoveFinishedItem->isChecked())
		delete batchList->takeItem(batchList->row(batchList->findItems(currentFilePath, Qt::MatchExactly)[0]));
	
	// no queue then finish
	if (queue.empty())
		return finishConvert();
	
	// start convert process
	currentFilePath = queue.dequeue();
	
	convert();
	
	// status bar
	status->setText(tr("converting..."));
	
	// remaining files
	progress->setText(QString(tr("converting: %1/%2")).arg(total - queue.size()).arg(total));
	
	// update current file
	currentFile->setText(pathToFilename(currentFilePath));
	currentProgressBar->setValue(0);
}

void TouchConv::finishConvert()
{
	// enable buttons
	unlockInterface();
	
	// status
	status->setText(tr("finished"));
	
	// finish alert
	if (queue.size() == 0)
		QMessageBox::information(this, tr("finished"), tr("all items had been converted"));
}

void TouchConv::startConvert()
{
	// new queue
	QStringList list = batchList->pathList();
	queue.clear();
	
	// no item
	if (list.size() == 0 || profile->currentIndex() < 0)
		return;
	
	// adding itmes to queue
	for (int i = 0; i != list.size(); ++i)
		queue.enqueue(list[i]);
	
	// item count
	total = list.size();
	
	// reset progress batr
	currentProgressBar->setValue(0);
	
	// disable user input
	lockInterface();
	
	// start convert process
	currentFilePath = queue.dequeue();
	
	convert();
	
	// total progress
	totalProgressBar->setRange(0, total);
	totalProgressBar->setValue(0);
	progress->setText(QString(tr("converting: %1/%2")).arg(total - queue.size()).arg(total));
	
	// update current file
	currentFile->setText(pathToFilename(currentFilePath));
	
	// elapsed time
	totalTime.start();
	
	// status bar
	status->setText(tr("converting..."));
}

void TouchConv::abort()
{
	status->setText(tr("aborting..."));
	
	// terminate
	status->setText(tr("terminating process...please wait"));
	ffmpeg.abort();
	
	// finish
	finishConvert();
	
	status->setText(tr("aborted"));
}

void TouchConv::convert()
{
	ffmpeg.setConverter(converter[profile->currentIndex()]);
	ffmpeg.convert(profileArgs[profile->currentIndex()].arg(currentFilePath).arg(outputPath(currentFilePath)));
}

void TouchConv::updateProgress(int p)
{
	if (!p)	return;
	
	currentProgressBar->setValue(p);
	
	totalElapsedTime->setText(ATime::fromSecs(totalTime.elapsed() / 1000).toString(tr("elapsed: %1:%2:%3")));
	
	// elapsed
	int elapsed = ffmpeg.timeElapsed() / 1000;
	timeElapsed->setText(ATime::fromSeconds(elapsed).toString(tr("elapsed: %1:%2:%3")));
	
	// estimated total
	int estimated = static_cast<int>(elapsed / static_cast<double>(p) * 100.0);
	estimatedTotalTime->setText(ATime::fromSeconds(estimated).toString(tr("total: %1:%2:%3")));
	
	// remain
	timeRemain->setText(ATime::fromSeconds(estimated - elapsed).toString(tr("remain: %1:%2:%3")));
}

void TouchConv::setSuffixForSelectedProfile(int index)
{
	outputSuffix->setCurrentIndex(outputSuffix->findText(profileSuffix[index]));
}

void TouchConv::changeOutputDirectory()
{
	QString path = QFileDialog::getExistingDirectory(this);
	
	if (path.isNull())
		return;
	
	outputDirectory->setText(path);
}

/*
	@brief	load setting profiles from dir
*/
void TouchConv::loadSettingProfiles(const QString &dir)
{
	QStringList settings = QDir(dir).entryList(QStringList() << "*.ini", QDir::Files | QDir::Readable);
	
	if (settings.size() == 0)
	{
		QMessageBox::critical(this, tr("no profile exists"), tr("no profile was found in \"profiles\""));
		return;
	}
	
	for (int i = 0; i != settings.size(); ++i)
	{
		// QSettings setting(dir + '/' + settings[i], QSettings::IniFormat);
		QSettings setting(QString("%1/%2").arg(dir).arg(settings[i]), QSettings::IniFormat);
		
		setting.beginGroup("profile");
		profile->insertItem(i, setting.value("name").toString());
		setting.endGroup();
		
		{
			setting.beginGroup("convert");
			
			// converter
			converter.insert(i, setting.value("converter").toString());
			
			// args
			profileArgs.insert(i, setting.value("args").toString());
			
			// suffix
			QString suffix = setting.value("suffix").toString();
			profileSuffix.insert(i, suffix);
			if (outputSuffix->findText(suffix) == -1)
				outputSuffix->addItem(setting.value("suffix").toString());
			
			setting.endGroup();
		}
	}
}

void TouchConv::lockInterface()
{
	batchList->setAcceptDrops(false);
	removeItemButton->setEnabled(false);
	clearButton->setEnabled(false);
	upButton->setEnabled(false);
	downButton->setEnabled(false);
	convertButton->setEnabled(false);
	abortButton->setEnabled(true);
	outputDirectory->setReadOnly(true);
	changeOutputDirectoryButton->setEnabled(false);
}

void TouchConv::unlockInterface()
{
	batchList->setAcceptDrops(true);
	clearButton->setEnabled(true);
	removeItemButton->setEnabled(true);
	upButton->setEnabled(true);
	downButton->setEnabled(true);
	convertButton->setEnabled(true);
	abortButton->setEnabled(false);
	outputDirectory->setReadOnly(false);
	changeOutputDirectoryButton->setEnabled(true);
}

QString TouchConv::pathToFilename(const QString &path)
{
	return path.right(path.size() - path.lastIndexOf('/') - 1);
}

// QLineEdit: user can edit suffix of output file
QString TouchConv::outputPath(const QString &filename) const
{
	if (outputDirectory->text().isEmpty())
		return filename.left(filename.lastIndexOf('.')) + outputSuffix->currentText();
	
	// generate full path
	QString path = outputDirectory->text();
	
	path.replace('\\', '/');
	
	if (!path.endsWith('/'))
		path.append('/');
	
	path.append(pathToFilename(filename));
	
	return path.left(path.lastIndexOf('.')) + outputSuffix->currentText();
}
